using System;
using System.Reflection;
using NUnit.Framework;
using ShadeTree.Binding.Validation;

namespace ShadeTree.Binding.Tests
{
    [TestFixture]
    public class ReflectionHelperTester
    {
        #region Setup/Teardown

        [SetUp]
        public void SetUp()
        {
            PropertyInfo top = ReflectionHelper.GetProperty<Target>(x => x.Child);
            PropertyInfo second = ReflectionHelper.GetProperty<ChildTarget>(x => x.GrandChild);
            PropertyInfo birthday = ReflectionHelper.GetProperty<GrandChildTarget>(x => x.BirthDay);

            _chain = new PropertyChain(new[] {top, second, birthday});
        }

        #endregion

        private PropertyChain _chain;

        #region inner classes

        public class Target
        {
            public string Name { get; set; }
            public ChildTarget Child { get; set; }
        }

        public class ChildTarget
        {
            public int Age { get; set; }
            public GrandChildTarget GrandChild { get; set; }
        }

        public class GrandChildTarget
        {
            public DateTime BirthDay { get; set; }
        }

        #endregion

        [Test]
        public void CreateAPropertyChainFromReflectionHelper()
        {

            Accessor accessor = ReflectionHelper.GetAccessor<Target>(x => x.Child.GrandChild.BirthDay);
            var target = new Target { Child = new ChildTarget { GrandChild = new GrandChildTarget { BirthDay = DateTime.Today } } };


            accessor.GetValue(target).ShouldEqual(DateTime.Today);
        }

        [Test]
        public void Try_to_fetch_a_method()
        {
            MethodInfo method = ReflectionHelper.GetMethod<SomeClass>(s => s.DoSomething());
            method.Name.ShouldEqual("DoSomething");
        }

        public class SomeClass
        {
            public object DoSomething()
            {
                return null;
            }
        }


        [Test]
        public void GetFieldNameFromSingleProperty()
        {
            SingleProperty property = SingleProperty.Build<Target>(x => x.Name);
            property.FieldName.ShouldEqual("Name");
        }

        [Test]
        public void GetNameFromSingleProperty()
        {
            SingleProperty property = SingleProperty.Build<Target>(x => x.Name);
            property.Name.ShouldEqual("Name");
        }

        [Test]
        public void GetValueFromSingleProperty()
        {
            var target = new Target {Name = "Jeremy"};
            SingleProperty property = SingleProperty.Build<Target>(x => x.Name);
            property.GetValue(target).ShouldEqual("Jeremy");
        }

        [Test]
        public void PropertyChainCanGetPropertyHappyPath()
        {
            var target = new Target
                             {Child = new ChildTarget {GrandChild = new GrandChildTarget {BirthDay = DateTime.Today}}};
            _chain.GetValue(target).ShouldEqual(DateTime.Today);
        }

        [Test]
        public void PropertyChain_can_get_the_name()
        {
            ReflectionHelper.GetAccessor<Target>(t => t.Child.GrandChild.BirthDay).Name.ShouldEqual("ChildGrandChildBirthDay");
        }


        [Test]
        public void PropertyChainCanSetPRopertyHappyPath()
        {
            var target = new Target
                             {Child = new ChildTarget {GrandChild = new GrandChildTarget {BirthDay = DateTime.Today}}};
            _chain.SetValue(target, DateTime.Today.AddDays(1));

            target.Child.GrandChild.BirthDay.ShouldEqual(DateTime.Today.AddDays(1));
        }

        [Test]
        public void PropertyChainGetPropertyReturnsNullForSadPath()
        {
            var target = new Target {Child = new ChildTarget()};
            _chain.GetValue(target).ShouldBeNull();
        }

        [Test]
        public void PropertyChainReturnsInnerMostFieldName()
        {
            _chain.FieldName.ShouldEqual("BirthDay");
        }

        [Test]
        public void PropertyChainReturnsInnerMostPropertyType()
        {
            _chain.PropertyType.ShouldEqual(typeof (DateTime));
        }

        [Test]
        public void SetValueFromSingleProperty()
        {
            var target = new Target {Name = "Jeremy"};
            SingleProperty property = SingleProperty.Build<Target>(x => x.Name);
            property.SetValue(target, "Justin");

            target.Name.ShouldEqual("Justin");
        }

        [Test]
        public void TryingToCallSetDoesNotBlowUpIfTheIntermediateChildrenAreNotThere()
        {
            var target = new Target {Child = new ChildTarget()};
            _chain.SetValue(target, DateTime.Today.AddDays(4));
        }

        [Test]
        public void SingleProperty_knows_how_to_validate_a_single_field()
        {
            ChildValidationTarget target = new ChildValidationTarget(){Name = null};
            Accessor accessor = ReflectionHelper.GetAccessor<ChildValidationTarget>(x => x.Name);

            accessor.Validate(target).Length.ShouldEqual(1);

            target.Name = "Scott Bellware";

            accessor.Validate(target).Length.ShouldEqual(0);
        }

        [Test]
        public void SingleProperty_knows_how_to_validate_a_single_field2()
        {
            ChildValidationTarget target = new ChildValidationTarget() { Name = null };
            Accessor accessor = ReflectionHelper.GetAccessor<ChildValidationTarget, string>(x => x.Name);

            accessor.Validate(target).Length.ShouldEqual(1);

            target.Name = "Scott Bellware";

            accessor.Validate(target).Length.ShouldEqual(0);
        }

        [Test]
        public void PropertyChain_knows_how_to_validate_a_single_field()
        {
            ValidationTarget target = new ValidationTarget(){Child = new ChildValidationTarget(){Name = null}};
            Accessor accessor = ReflectionHelper.GetAccessor<ValidationTarget>(x => x.Child.Name);

            accessor.Validate(target).Length.ShouldEqual(1);

            target.Child.Name = "Scott Bellware";

            accessor.Validate(target).Length.ShouldEqual(0);
        }
    }




    public class ValidationTarget
    {
        public ChildValidationTarget Child { get; set;}
    }

    public class ChildValidationTarget
    {
        [Required]
        public string Name{ get; set;}
    }
}